System virtualization is the abstraction and pooling of resources on a platform. The abstraction decouples software and hardware and enables multiple operating systems to execute concurrently on a single physical platform without interfering with each other. To permit operating systems to execute on the same physical platform, a platform layer implemented in software decouples the operating system from the underlying hardware. This platform layer is referred to as a hypervisor and the operating system is referred to as a guest operating system.
To provide protection and isolation between guest operating systems and the hypervisor, the hypervisor controls address translation on the hardware (e.g., a processor) when guest operating systems are active. This level of address translation maps the guest operating system's view of the physical memory of the processor to the hypervisor's view of the physical memory. Software-based techniques maintain a shadow version of a page table (e.g., a data structure used by a virtual memory system in an operating system to store a mapping between virtual addresses and physical addresses) derived from a guest page table. The shadow version of the page table is referred to as a shadow page table. The shadow page table is managed by the hypervisor. When a guest operating system is active, the shadow page table is used to perform address translation. The shadow page table is not visible to the guest operating system, and the guest operating system is forced to use the guest page table to perform address translation.
To maintain a valid shadow page table, the hypervisor keeps track of the state of the guest page table. This includes modifications by the guest operating system to add or remove translations in the guest page table, guest operating system and/or hypervisor induced page faults (e.g., referencing memory that cannot be referenced), accessed and dirty bits in the shadow page table, etc. The hypervisor ensures that the guest page table is mapped correctly to the shadow page table, especially when a guest operating system is switching from one stack to another stack (e.g., a last in, first out (LIFO) abstract data type and linear data structure). Otherwise, the guest operating system will experience a page fault when attempting to switch to the other stack.
One method to ensure that the guest operating system does not experience a page fault may include retrieving a new stack before every stack pointer changing instruction. However, such a method may incur high overhead due to the high frequency of stack push and pop instructions. Another method may include retrieving a new stack before every stack load instruction (e.g., a move instruction, a leave instruction, an exchange instruction, etc.). This method may address the problems associated with the stack push and pop instructions, but experiences difficulties when the stacks are multiple pages. A problem with multiple page stacks is that all of the current stack pages may need to be mapped. This is difficult for the hypervisor to accomplish because at the time of execution by the guest operating system, the hypervisor is unaware, given a new stack pointer, which pages are part of the stack. For example, the hypervisor may know that the new stack pointer is part of the stack but may not know whether a previous page or a next page is part of the stack. However, all pages of a given stack may need to be mapped by the hypervisor for stack push and pop instructions to execute unchecked.